package control;

import java.util.LinkedList;
import java.util.List;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import model.CompileError;
import model.ErrorType;
import model.Token;
import model.Token.TokenType;
import model.Token.Value;

/**
 * Controlador responsável pela gestão dos autômatos de reconhecimento dos tokens,
 * essa classe utiliza como "entrada" um atributo <code>File</code>, que deve apontar
 * para um arquivo de texto. Faz, então uma leitura desse arquivo e o bufferiza numa 
 * <code>String</code>, para acessá-la índice à índice e identificar o autômato correto
 * para tratar cada situação.
 * 
 * <p>Após utilizar lógica interna e identificar tokens e errors léxicos, tem como "saída"
 * dois atributos <code>List</code>: um para os tokens reconhecidos e outro para os erros
 * identificados. Utiliza para isso as classes <code>Token</code> e <code>LexicError</code>.
 * 
 * @author 	Jody Matos, Alenilson
 * @version 1.6
 * @see model.Token
 * @see model.CompileError
 * @since	10/18/2011
 * 
 * <dt><b>History:</b></dt>
 * 	<dd>1.0: Creation of first methods</dd>
 *  <dd>1.1: Update of overall methods adapting him to our project instead auto-generated 
 *  	code</dd>
 *  <dd>1.2: Reading  a File and showing it on the main window</dd>
 *  <dd>1.3: Recognizing Identifiers, Delimiters, Numbers and Operators</dd>
 *  <dd>1.4: Recognizing the others tokens</dd>
 *  <dd>1.5: Showing results on the main window</dd>
 *  <dd>1.6: Adjust 'comma' like a delimiter</dd>
 */
public class ControllerLexicAnalyzer {
	private File 				sourceFile;
	private String 				sourceCode;
	private String				lexeme;
	private int 				currentIndex;
	private int					lineNumber;
	private char				currentSymbol;
	private List<Token>			tokens;
	private List<CompileError> 	errors;
	
	/**
	 * Cria uma nova instância de <code>LexicAnalyzer</code>, sem a necessidade de passagem
	 * de parâmetros. Inicia as listas de tokens e errors léxicos, além das <code>Strings</code>
	 * de código fonte e lexema, todos vazios, bem como o índice inicial de varredura do código
	 * fonte em <code>0</code> e o atributo de controle de número de linha em <code>1</code>.
	 */
	public ControllerLexicAnalyzer() {
		this.tokens = new LinkedList<Token>();
		this.errors = new LinkedList<CompileError>();
		this.sourceCode = "  ";
		this.lexeme = "";
		this.currentIndex = 0;
		this.lineNumber=1;
	}

	public File getSourceFile() {
		return this.sourceFile;
	}

	public String getSourceCode() {
		this.readSource();
		return this.sourceCode;
	}

	public List<Token> getTokens() {
		return this.tokens;
	}
	
	public List<CompileError> getErrors(){
		return this.errors;
	}

	public void setSource(File source) {
		this.sourceFile = source;
	}

	/**
	 * Estado inicial do autômato, este método gere o início da identificação de cada um dos lexemas,
	 * direcionando-os para os devidos identificadores de tokens. Sua tomada de decisão é baseada no 
	 * primeiro caractere de cada um dos lexemas. Retorna <code>true</code> se todo o código fonte
	 * for analisado e não for encontrado nenhum erro de formação ou erro léxico. Caso contrário,
	 * retorna <code>false</code>. Em sua lógica, bem como na dos demais métodos que implementam o
	 * autômato, utiliza os atributos <code>currentIndex</code> e <code>currentSymbol</code> para
	 * varrer todo o arquivo fonte e realizar a identificação dos tokens.
	 * 
	 * @return <code>true</code> se o código for varrido por completo e não for encontrado erro.
	 * 		   <dd><code>false</code> caso contrário
	 */
	public boolean scan() {
		while(currentIndex<sourceCode.length()){
			if(!isBlank((currentSymbol=sourceCode.charAt(currentIndex)))){
				lexeme="  ";
				if(isLetter(currentSymbol)) {
					consumeSymbol();
					if(!scanIdentifier()){
						System.out.println("erro de formação de identificador");
					}
				}
				else if(isEOL(currentSymbol)){
					lineNumber++;
					currentIndex++;
				}
				else if(isDigit(currentSymbol)){
					consumeSymbol();
					if(!scanNumber()){
						System.out.println("erro de formação de numero");
					}
				}
				else if(isOperator(currentSymbol)){
					if(!scanOperator()){
						System.out.println("erro de formação de operador");
					}
				}
				else if(isDelimiter(currentSymbol)){
					if(!scanDelimiter()){
						System.out.println("erro de formação de delimitador");
					}
				}
				else if (currentSymbol=='\"'){
					consumeSymbol();
					if(!scanString()){
						System.out.println("erro de formação de cadeia constante");
					}
				}
				else if (currentSymbol=='\''){
					consumeSymbol();
					if(!scanChar()){
						System.out.println("erro de formação de caractere constante");
					}
				}
				else{
					consumeSymbol();
					CompileError error = new CompileError(lexeme, ErrorType.UNEXPECTED_SYMBOL, lineNumber);
					errors.add(error);
					System.out.println("erro léxico: "+currentSymbol);
				}
			}
			else currentIndex++;
		}
		return false;
	}

	/**
	 * Lê o arquivo fonte, indicado por <code>sourceFile</code>, e bufferiza todo o texto num
	 * único atributo, o <code>sourceCode</code>.
	 */
	private void readSource() {
		this.sourceCode = "  ";
		this.currentIndex = 0;
		this.lineNumber=1;
		this.tokens.clear();
		this.errors.clear();
		try {
			BufferedReader in = new BufferedReader(new FileReader(this.sourceFile));
			String line;
			while((line=in.readLine())!=null){
				sourceCode = sourceCode+line+"\n  ";
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, até
	 * que seja encontrado um caratere que não seja Letra, Dígito ou '_', baseando-se
	 * na regra de formação de Identificador ('L' indicando letra; 'D' indicando
	 * dígito; e '_' o próprio caractere): L(L|D|_)*
	 * 
	 * <p>Ao final, se houver caractere estranho compondo o lexema, cria um objeto
	 * <code>LexicError</code> contendo as informações do erro e retorna <code>false</code>. 
	 * Caso contrário, cria um objeto <code>Token</code> contendo as informações deste
	 * indetificador e retorna <code>true</code>, indicando que a identificação do 
	 * token foi finalizada com sucesso. 
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanIdentifier() {
		while(isLetter(currentSymbol=sourceCode.charAt(currentIndex)) || 
				isDigit(currentSymbol) || currentSymbol=='_')
			consumeSymbol();
		if(isReserved(lexeme)){
			/*Token reserved = new Token(lexeme, Value.RESERVED, TokenType.RESERVED);
			tokens.add(reserved);
			return true;*/
			return scanReserved();
		}
		if(!isSymbol(currentSymbol=sourceCode.charAt(currentIndex)) &&
				!isEOL(currentSymbol) &&
				!isBlank(currentSymbol)){
			consumeSymbol();
			while(currentIndex<sourceCode.length() && 
					!isBlank(currentSymbol=sourceCode.charAt(currentIndex)) && 
					!isEOL(currentSymbol))
				consumeSymbol();
			CompileError error = new CompileError(lexeme, ErrorType.IDENTIFIER_FORMING, lineNumber);
			errors.add(error);
			return false;
		}
		Token identifier = new Token(lexeme, Value.IDENTIFIER, TokenType.IDENTIFIER, lineNumber);
		tokens.add(identifier);
		return true;
	}
	
	private boolean scanReserved(){
		if(lexeme.equals("  variaveis")){
			Token reserved = new Token(lexeme, Value.VARIABLES, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  metodos")){
			Token reserved = new Token(lexeme, Value.METHODS, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  constantes")){
			Token reserved = new Token(lexeme, Value.CONSTANTS, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  classe")){
			Token reserved = new Token(lexeme, Value.CLASS, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  retorno")){
			Token reserved = new Token(lexeme, Value.RETURN, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  vazio")){
			Token reserved = new Token(lexeme, Value.NULL, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  principal")){
			Token reserved = new Token(lexeme, Value.MAIN, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  se")){
			Token reserved = new Token(lexeme, Value.IF, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if( lexeme.equals("  entao")){
			Token reserved = new Token(lexeme, Value.THEN, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  senao")){
			Token reserved = new Token(lexeme, Value.ELSE, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  enquanto")){
			Token reserved = new Token(lexeme, Value.WHILE, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  para")){
			Token reserved = new Token(lexeme, Value.FOR, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  leia")){
			Token reserved = new Token(lexeme, Value.READ, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  escreva")){
			Token reserved = new Token(lexeme, Value.PRINT, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  inteiro")){
			Token reserved = new Token(lexeme, Value.INT, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  real")){
			Token reserved = new Token(lexeme, Value.FLOAT, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  logico")){
			Token reserved = new Token(lexeme, Value.BOOL, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  caractere")){
			Token reserved = new Token(lexeme, Value.CHAR, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  cadeia")){
			Token reserved = new Token(lexeme, Value.STRING, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  verdadeiro")){
			Token reserved = new Token(lexeme, Value.TRUE, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  falso")){
			Token reserved = new Token(lexeme, Value.FALSE, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		else if(lexeme.equals("  herda_de")){
			Token reserved = new Token(lexeme, Value.EXTENDS, TokenType.RESERVED, lineNumber);
			tokens.add(reserved);
			return true;
		}
		return false;
	}

	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, até
	 * que seja encontrado um caratere que não seja Dígito.Caso esse caractere seja um
	 * ponto, ainda há possibilidade ser um número real, necessitando consumir mais 
	 * dígitos. Caso contrário, gera um erro de formação de número, baseando-se na regra
	 * de formação de número da linguagem em questão ('D' indicando dígito): D+(.D+)?
	 * 
	 * <p>Ao final, caso seja identificado um número, cria um objeto do tipo <code>Token></code>
	 * contendo as informações desde número e retorna <code>true</code>, indicado o sucesso
	 * na identificação. Caso contrário, cria um objeto <code>LexicError</code> informando
	 * as informações necessárias pra caracterização do erro.
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanNumber(){
		while(isDigit(currentSymbol=sourceCode.charAt(currentIndex)))
			consumeSymbol();
		if((currentSymbol=sourceCode.charAt(currentIndex))=='.'){
			consumeSymbol();
			if(isDigit(currentSymbol=sourceCode.charAt(currentIndex))){
				consumeSymbol();
				while(isDigit(currentSymbol=sourceCode.charAt(currentIndex)))
					consumeSymbol();
				Token number = new Token(lexeme, Value.NUM_FLOAT, TokenType.NUMBER, lineNumber);
				tokens.add(number);
				return true;
			}
			else{
				while(currentIndex<sourceCode.length() && !isBlank(currentSymbol=sourceCode.charAt(currentIndex)) && !isEOL(currentSymbol))
					consumeSymbol();
				CompileError error = new CompileError(lexeme, ErrorType.NUMBER_FORMING, lineNumber);
				errors.add(error);
				return false;
			}
		}
		Token number = new Token(lexeme, Value.NUM_INT, TokenType.NUMBER, lineNumber);
		tokens.add(number);
		return true;
	}

	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, em busca
	 * de expressões indicadas nas regras de formação de operador da Linguagem: 
	 * + ++ - -- = == != < <= > >= * / && || . 
	 * 
	 * <p>Há, ainda, a possibilidade de identificação de provável comentário, iniciando em
	 * momento oportuno a identificação desse tipo de token.
	 * 
	 * <p>Ao final, caso seja identificado um operador, cria um objeto do tipo <code>Token></code>
	 * contendo as informações desde tipo de token e retorna <code>true</code>, indicado o sucesso
	 * na identificação. Caso contrário, cria um objeto <code>LexicError</code> informando
	 * as informações necessárias pra caracterização do erro.
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanOperator() {
		if(currentSymbol=='+'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='+'){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.INC, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			Token operator = new Token(lexeme, Value.PLUS, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		else if(currentSymbol=='-'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='-'){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.DEC, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			Token operator = new Token(lexeme, Value.MINUS, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		else if(currentSymbol=='='){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='='){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.ISEQUAL, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			Token operator = new Token(lexeme, Value.EQ, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		else if(currentSymbol=='>'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='='){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.HEQ, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			Token operator = new Token(lexeme, Value.HIGH, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		else if(currentSymbol=='<'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='='){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.LEQ, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			Token operator = new Token(lexeme, Value.LESS, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		else if(currentSymbol=='!'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='='){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.DIF, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			CompileError error = new CompileError(lexeme, ErrorType.OPERATOR_FORMING, lineNumber);
			errors.add(error);
			return false;
		}
		else if(currentSymbol=='/'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='/' || currentSymbol=='*'){
				return scanComment();
			}
			Token operator = new Token(lexeme, Value.DIV, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		else if(currentSymbol=='&'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='&'){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.AND, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			CompileError error = new CompileError(lexeme, ErrorType.NUMBER_FORMING, lineNumber);
			errors.add(error);
			return false;
		}
		else if(currentSymbol=='|'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='|'){
				consumeSymbol();
				Token operator = new Token(lexeme, Value.OR, TokenType.OPERATOR, lineNumber);
				tokens.add(operator);
				return true;
			}
			CompileError error = new CompileError(lexeme, ErrorType.NUMBER_FORMING, lineNumber);
			errors.add(error);
			return false;
		}
		else if(currentSymbol=='.'){
			consumeSymbol();
			Token operator = new Token(lexeme, Value.DOT, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		else if(currentSymbol=='*'){
			consumeSymbol();
			Token operator = new Token(lexeme, Value.MULT, TokenType.OPERATOR, lineNumber);
			tokens.add(operator);
			return true;
		}
		return false;
	}

	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, em busca
	 * de expressões indicadas nas regras de formação de delimitador da Linguagem: 
	 * ; ( ) { } [ ] ,
	 * 
	 * <p>Ao final, caso seja identificado um delimitador, cria um objeto do tipo <code>Token></code>
	 * contendo as informações desde tipo de token e retorna <code>true</code>, indicado o sucesso
	 * na identificação. Caso contrário, cria um objeto <code>LexicError</code> informando
	 * as informações necessárias pra caracterização do erro.
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanDelimiter() {
		if(currentSymbol==';'){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.SEMICOLON, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		else if(currentSymbol=='('){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.OPENPAR, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		else if(currentSymbol==')'){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.CLOSEPAR, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		else if(currentSymbol=='['){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.OPENBRA, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		else if(currentSymbol==']'){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.CLOSEBRA, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		else if(currentSymbol=='{'){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.OPENKEY, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		else if(currentSymbol=='}'){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.CLOSEKEY, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		else if(currentSymbol==','){
			consumeSymbol();
			Token delimiter = new Token(lexeme, Value.COMMA, TokenType.DELIMITER, lineNumber);
			tokens.add(delimiter);
			return true;
		}
		return false;
	}

	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, até
	 * que seja encontrado um caratere que não seja Símbolo, o qual está definido como
	 * caracteres entre os códigos ASCII 32 e 126, exceto o 34, como indica a regra
	 * de formação deste token: "(L|D|S)*"
	 * 
	 * <p>Ao final, cria um objeto <code>Token</code> contendo as informações desta
	 * cadeia constante e retorna <code>true</code>, indicando que a identificação do 
	 * token foi finalizada com sucesso (não há erro de formação de identificador). 
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanString() {
		while(isSymbol(currentSymbol=sourceCode.charAt(currentIndex)))
			consumeSymbol();
		if((currentSymbol=sourceCode.charAt(currentIndex))=='\"'){
			consumeSymbol();
			Token string = new Token(lexeme, Value.STRING, TokenType.STRING, lineNumber);
			tokens.add(string);
			return true;
		}
		while(currentIndex<sourceCode.length() && (currentSymbol=sourceCode.charAt(currentIndex))!='\"' && !isEOL(currentSymbol))
			consumeSymbol();
		if(currentSymbol=='\"')
			consumeSymbol();
		CompileError error = new CompileError(lexeme, ErrorType.STRING_FORMING, lineNumber);
		errors.add(error);
		return false;
	}

	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, em busca
	 * de tokens que obedeçam sua regra de formação: // isto é um comentário de linha
	 * 
	 * <p>Ao final, cria um objeto <code>Token</code> contendo as informações deste
	 * comentário e retorna <code>true</code>, indicando que a identificação do 
	 * token foi finalizada com sucesso. Caso contrário, cria um objeto de erro. 
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanComment() {
		if((currentSymbol=sourceCode.charAt(currentIndex))=='/'){
			consumeSymbol();
			while(currentIndex<sourceCode.length() && !isEOL(currentSymbol=sourceCode.charAt(currentIndex)))
				consumeSymbol();
			return true;
		}
		else if(currentSymbol=='*'){
			return scanBlockComment();
		}
		CompileError error = new CompileError(lexeme, ErrorType.STRING_FORMING, lineNumber);
		errors.add(error);
		return false;
	}
	
	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, em busca
	 * de tokens que obedeçam sua regra de formação: /* Isto é um comentário de bloco* /
	 * 
	 * <p>Ao final, cria um objeto <code>Token</code> contendo as informações deste
	 * comentário e retorna <code>true</code>, indicando que a identificação do 
	 * token foi finalizada com sucesso. Caso contrário, cria um objeto de erro. 
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanBlockComment(){
		consumeSymbol();
		while(currentIndex<sourceCode.length() && (currentSymbol=sourceCode.charAt(currentIndex))!='*'){
			if(isEOL(currentSymbol))
				lineNumber++;
			consumeSymbol();
		}
		if(currentIndex<sourceCode.length() && (currentSymbol=sourceCode.charAt(currentIndex))=='*'){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='/'){
				consumeSymbol();
				//Token comment = new Token(lexeme, Value.COMMENT, TokenType.COMMENT);
				//tokens.add(comment);
				return true;
			}
			else{
				if(isEOL(currentSymbol))
					lineNumber++;
				return scanBlockComment();
			}
		}
		else{
			CompileError error = new CompileError(lexeme, ErrorType.COMMENT_FORMING, lineNumber);
			errors.add(error);
			return false;
		}
	}
	
	/**
	 * Varre o arquivo fonte à partir do índice atual <code>currentIndex</code>, em busca
	 * de tokens que obedeçam sua regra de formação (S indicando símbolo): 'S'
	 * 
	 * <p>Ao final, cria um objeto <code>Token</code> contendo as informações deste
	 * token e retorna <code>true</code>, indicando que sua identificação foi finalizada 
	 * com sucesso. Caso contrário, cria um objeto de erro. 
	 * 
	 * @return <code>true</code> se a identificação ocorrer com sucesso
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean scanChar(){
		if(isSymbol(currentSymbol=sourceCode.charAt(currentIndex))){
			consumeSymbol();
			if((currentSymbol=sourceCode.charAt(currentIndex))=='\''){
				consumeSymbol();
				Token character = new Token(lexeme, Value.CHAR, TokenType.CHAR, lineNumber);
				tokens.add(character);
				return true;
			}
		}
		while(currentIndex<sourceCode.length() && (currentSymbol=sourceCode.charAt(currentIndex))!='\'' && !isEOL(currentSymbol)){
			consumeSymbol();
		}
		if(currentSymbol=='\'')
			consumeSymbol();
		CompileError error = new CompileError(lexeme, ErrorType.CHAR_FORMING, lineNumber);
		errors.add(error);
		return false;
	}

	/**
	 * Testa se <code>symbol</code> é um caractere que indica uma letra no alfabeto em questão.
	 * Para o caso, letras são todos os caracteres com código ASCII maiores que 65 e menores 90, bem
	 * com os maiores que 97 e menores que 122.
	 * 
	 * @param symbol O símbolo a ser testado.
	 * @return <code>true</code> se o caractere for uma letra
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isLetter(char symbol) {
		if((symbol>=65 && symbol<=90)||(symbol>=97 && symbol<=122))
			return true;
		return false;
	}

	/**
	 * Testa se <code>symbol</code> é um caractere que indica um dígito no alfabeto em questão.
	 * Para o caso, dígitos são todos os caracteres com código ASCII maiores que 48 e menores 57.
	 * 
	 * @param symbol O símbolo a ser testado.
	 * @return <code>true</code> se o caractere for um dígito
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isDigit(char symbol) {
		if(symbol>=48 && symbol<=57)
			return true;
		return false;
	}

	/**
	 * Testa se <code>symbol</code> é um caractere usado como, ou que compõe, um operador no alfabeto em questão.
	 * Para o caso, esses carateres são: + - * / = < > ! & | .  
	 * 
	 * @param symbol O símbolo a ser testado.
	 * @return <code>true</code> se o caractere for, ou compor, um operador.
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isOperator(char symbol) {
		if(symbol=='+' || symbol=='-' || symbol=='*' || 
				symbol=='/' || symbol=='=' || symbol=='<' || 
				symbol=='>' || symbol=='!' || symbol=='&' || 
				symbol=='|' || symbol=='.')
			return true;
		return false;
	}

	/**
	 * Testa se <code>symbol</code> é um caractere usado como um delimitador no alfabeto em questão.
	 * Para o caso, esses carateres são: ; ( ) { } [ ] ,
	 * 
	 * @param symbol O símbolo a ser testado.
	 * @return <code>true</code> se o caractere for um delimitador.
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isDelimiter(char symbol) {
		if(symbol==';' || symbol=='(' || symbol==')' || 
				symbol=='{' || symbol=='}' || symbol=='[' || 
				symbol==']' || symbol==',')
			return true;
		return false;
	}
	
	/**
	 * Testa se <code>symbol</code> é um caractere em branco numa linha (espaço em branco ou tabulalção
	 * horizontal). 
	 * 
	 * @param symbol O símbolo a ser testado.
	 * @return <code>true</code> se for caractere em branco numa linha
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isBlank(char symbol){
		if(symbol==' ' || symbol=='\t')
			return true;
		return false;
	}
	
	/**
	 * Testa se <code>symbol</code> é um caractere de fim de linha (EOL: \n, \r ou \f).
	 * 
	 * @param symbol O símbolo a ser testado.
	 * @return <code>true</code> se for caractere de fim de linha
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isEOL(char symbol){
		if(symbol=='\n' || symbol=='\r' || symbol=='\f')
			return true;
		return false;
	}
	
	/**
	 * Testa se <code>symbol</code> é um caractere definido como símbolo na linguagem em
	 * questão: ASCII maior que 32 e menor 126, exceto o 34. 
	 * 
	 * @param symbol O símbolo a ser testado.
	 * @return <code>true</code> se for o caractere for um símbolo
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isSymbol(char symbol){
		if(symbol>=32 && symbol<=126 && symbol!=34)
			return true;
		return false;
	}
	
	/**
	 * Teste se <code>lexeme</code> é uma palavra reservada da linguagem em questão.
	 * 
	 * @param lexeme O lexema a ser testato
	 * @return <code>true</code> se for uma palavra reservadaa
	 *		   <dd><code>false</code> caso contrário
	 */
	private boolean isReserved(String lexeme){
		if(lexeme.equals("  variaveis") || lexeme.equals("  metodos") || lexeme.equals("  constantes") ||
				lexeme.equals("  classe") || lexeme.equals("  retorno") || lexeme.equals("  vazio") || 
				lexeme.equals("  principal") || lexeme.equals("  se") || lexeme.equals("  entao") || 
				lexeme.equals("  senao") || lexeme.equals("  enquanto") || lexeme.equals("  para") || 
				lexeme.equals("  leia") || lexeme.equals("  escreva") || lexeme.equals("  inteiro") ||
				lexeme.equals("  real") || lexeme.equals("  logico") || lexeme.equals("  caractere") || 
				lexeme.equals("  cadeia") || lexeme.equals("  verdadeiro") || lexeme.equals("  falso") || 
				lexeme.equals("  herda_de"))  
			return true;
		return false;
	}
	
	/**
	 * Usado pelo autômato para consumir um símbolo nos testes de identificação do lexema:
	 * concatena o símbolo atual com os demais que, por ventura, compuserem o mesmo lexema
	 * e incrementa o valor de <code>currentIndex</code>. 
	 */
	private void consumeSymbol(){
		lexeme += currentSymbol;
		currentIndex++;
	}
}